part of internal;

class Actions {
   Buffer _buf;
   Cursor _cur;
   Selection _sel;
   Screen _scr;
   int _far;
   int _width;

   // generator
   Actions (int height, int width) {
      _buf = new Buffer();
      _cur = new Cursor(_buf.rend);
      _sel = new Selection(_buf.rend);
      _scr = new Screen(_buf.rend, height, width);
      _far = 0;
      _width = max(_scr.width, _buf.width);
      _cur.init(_buf, new Position(0, 0));
   }

   // mutators
   void left () {
      if (_cur.active) {
         _cur.left(_buf);
         _far = _cur.pos.ch;
      }
      else {
         _cur.init(_buf, _sel.start);
         _sel.kill();
         _far = _cur.pos.ch;
      }
      _update();
   }
   void right () {
      if (_cur.active) {
         _cur.right(_buf);
         _far = _cur.pos.ch;
      }
      else {
         _cur.init(_buf, _sel.end);
         _sel.kill();
         _far = _cur.pos.ch;
      }
      _update();
   }
   void up () {
      if (_cur.active) {
        _cur.up(_buf, _far);
        _update();
      }
      else left();
   }
   void down () {
      if (_cur.active) {
         _cur.down(_buf, _far);
         _update();
      }
      else right();
   }

   void shiftLeft () {
      if (_sel.active) {
         _sel.left(_buf, _width);
         _far = _sel.head.ch;
      }
      else {
         _sel.init(_buf, _width, _cur.pos, _cur.pos);
         _cur.kill();
         _sel.left(_buf, _width);
         _far = _sel.head.ch;
      }
      if (_sel.cc == 0) {
         _cur.init(_buf, _sel.start);
         _sel.kill();
      }
      _update();
   }
   void shiftRight () {
      if (_sel.active) {
         _sel.right(_buf, _width);
         _far = _sel.head.ch;
      }
      else {
         _sel.init(_buf, _width, _cur.pos, _cur.pos);
         _cur.kill();
         _sel.right(_buf, _width);
         _far = _sel.head.ch;
      }
      if (_sel.cc == 0) {
         _cur.init(_buf, _sel.start);
         _sel.kill();
      }
      _update();
   }
   void shiftUp () {
      if (_sel.active) _sel.up(_buf, _width, _far);
      else {
         _sel.init(_buf, _width, _cur.pos, _cur.pos);
         _cur.kill();
         _sel.up(_buf, _width, _far);
      }
      if (_sel.cc == 0) {
         _cur.init(_buf, _sel.start);
         _sel.kill();
      }
      _update();
   }
   void shiftDown () {
      if (_sel.active) _sel.down(_buf, _width, _far);
      else {
         _sel.init(_buf, _width, _cur.pos, _cur.pos);
         _cur.kill();
         _sel.down(_buf, _width, _far);
      }
      if (_sel.cc == 0) {
         _cur.init(_buf, _sel.start);
         _sel.kill();
      }
      _update();
   }

   void character (String c) {
      if (_cur.active) _insert(c);
      else _replace(c);
      _update();
   }
   void backspace () {
      if (_cur.active && _cur.pos.ln == 0 && _cur.pos.ch == 0) return;
      if (_cur.active) _delete(1);
      else _replace('');
      _update();
   }

   String cut () {
      if (_cur.active) return '';
      else {
         String tmp = _buf.read(_sel.cc, _sel.start);
         _replace('');
         _update();
         return tmp;
      }
   }
   String copy () {
      if (_cur.active) return '';
      else return _buf.read(_sel.cc, _sel.start);
   }
   void paste (String seq) {
      if (_cur.active) _insert(seq);
      if (_sel.active) _replace(seq);
      _update();
   }

   // internal
   void _insert (String seq) {
      if (_sel.active) unsErr(this);
      Position pos = _cur.pos;
      _cur.kill();
      Position ret = _buf.insert(seq, pos);
      _cur.init(_buf, ret);
   }
   void _delete (int cnt) {
      if (_sel.active) unsErr(this);
      Position pos = _cur.pos;
      _cur.kill();
      Position ret = _buf.delete(cnt, pos);
      _cur.init(_buf, ret);
   }
   void _replace (String seq) {
      if (_cur.active) unsErr(this);
      int cnt = _sel.cc;
      Position pos = _sel.end;
      _sel.kill();
      Position tmp = _buf.delete(cnt, pos);
      Position ret = _buf.insert(seq, tmp);
      _cur.init(_buf, ret);
   }

   void _update () {
//      _scr.redraw(_buf.rend);
      if (_cur.active) {
         _scr.makeVisible(_buf.rend, _cur.pos.ln);
//         if (_scr.visible(_cur.pos.ln));
//         else _scr.makeVisible(_buf.rend, _cur.pos.ln);
      }
      else {
         _scr.makeVisible(_buf.rend, _sel.head.ln);
//         if (_scr.visible(_sel.head.ln));
//         else _scr.makeVisible(_buf.rend, _sel.head.ln);
      }
   }

   // observers
   PreElement get pe => _scr.pe;

/*
   // invariants
   bool get valid {
      if (_grey == null) return false;
      if (_cur == null) return false;
      if (_sel == null) return false;
      if (_far == null) return false;
      if (_buf == null) return false;
      if (_cur.ln >= _buf.lc) return false;
      if (_cur.ch > _buf.nthLine(_cur.ln).raw.length) return false;
      if (_cur.ch > _far) return false;
      return true;
   }

   // printing
   String toString () {
      if (_grey) return 'selection -> $_sel\nfar -> $_far';
      else return 'cursor -> $_cur\nfar -> $_far';
   }
   */
}
